package br.ufpe.cin.imlm2.jbeat.soot.throwableset;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import br.ufpe.cin.imlm2.jbeat.soot.common.CommonOptions;
import br.ufpe.cin.imlm2.jbeat.soot.common.MethodTagger;
import br.ufpe.cin.imlm2.jbeat.soot.common.filters.ApplicationExceptionFilter;
import br.ufpe.cin.imlm2.jbeat.soot.common.filters.CheckedExceptionFilter;
import br.ufpe.cin.imlm2.jbeat.soot.common.filters.CompositeSootClassFilter;
import br.ufpe.cin.imlm2.jbeat.soot.common.filters.CustomExceptionFilter;
import br.ufpe.cin.imlm2.jbeat.soot.common.filters.LibraryExceptionsFilter;
import br.ufpe.cin.imlm2.jbeat.soot.common.filters.UncheckedExceptionFilter;
import br.ufpe.cin.imlm2.jbeat.soot.throwableset.tags.ThrowableSetTag;
import br.ufpe.cin.imlm2.util.filter.CompositeFilter;

public class ThrowableSetTagger extends MethodTagger {

	/**
	 * Filter for exceptions that may be ignored.
	 */
	protected CompositeFilter<SootClass> filter;

	public ThrowableSetTagger( CommonOptions options ) {
		super( options );
	}

	public ThrowableSetTagger() {
		super();
	}

	@Override
	protected void executePreTaggingTransformations() {
		super.executePreTaggingTransformations();
		this.filter = createFilters();
	}

	@Override
	protected void checkTags( SootMethod m ) {
		if ( !m.hasTag( ThrowableSetTag.TAG_NAME ) ) {
			m.addTag( new ThrowableSetTag() );
		}
	}

	protected CompositeSootClassFilter createFilters() {
		CompositeSootClassFilter compositeFilter = new CompositeSootClassFilter();
		List<String> filterNames = ((CommonOptions) this.options).getFilters();
		for ( String filter : filterNames ) {
			if ( filter.equals( CommonOptions.FILTER_APPLICATION ) )
				compositeFilter.addFilter( new ApplicationExceptionFilter() );
			else if ( filter.equals( CommonOptions.FILTER_CHECKED ) )
				compositeFilter.addFilter( new CheckedExceptionFilter() );
			else if ( filter.equals( CommonOptions.FILTER_UNCHECKED ) )
				compositeFilter.addFilter( new UncheckedExceptionFilter() );
			else if ( !filter.equals( CommonOptions.FILTER_LIBRARY ) ) {
				SootClass filteredException = Scene.v().getSootClass( filter );
				compositeFilter.addFilter( new CustomExceptionFilter( filteredException ) );
			}
		}
		return compositeFilter;
	}

	protected Set<SootClass> filterExceptions( Set<SootClass> exceptions, SootMethod src ) {
		List<String> filterNames = ((CommonOptions) this.options).getFilters();
		Collection<SootClass> filteredExceptions = null;
		final LibraryExceptionsFilter libraryExceptionsFilter = new LibraryExceptionsFilter( src );
		if ( filterNames.contains( CommonOptions.FILTER_LIBRARY ) ) {
			this.filter.addFilter( libraryExceptionsFilter );
		}
		filteredExceptions = this.filter.filter( exceptions );
		this.filter.removeFilter( libraryExceptionsFilter );
		exceptions = new HashSet<SootClass>( filteredExceptions );
		return exceptions;
	}
}
